package com.wkq.util

import android.content.Context
import android.os.Process
import android.util.Log
import com.blankj.utilcode.util.DeviceUtils
import com.snail.antifake.jni.EmulatorDetectUtil
import java.io.BufferedReader
import java.io.File
import java.io.FileReader
import java.io.IOException
import java.lang.ref.WeakReference
import java.net.NetworkInterface
import java.util.Enumeration
import java.util.Locale

/**
 * 虚拟环境检测工具类
 *
 * 通过多维度检测手段判断App是否运行于模拟器、多开空间或虚拟环境中
 *
 * @Author: wkq
 * @Time: 2025/7/29 10:37
 * @Desc:
 */
object VirtualUtil {

    /**
     * 是否运行在模拟器中
     * 结合 DeviceUtils 和 自定义 JNI 检测接口进行判断
     *
     * @param context Context 对象，部分检测需要
     * @return true 表示模拟器环境
     */
    fun isRunningInEmulator(context: Context?): Boolean {
        try {
            // 使用弱引用避免内存泄漏
            val contextWR = WeakReference<Context?>(context)

            // 使用第三方库 DeviceUtils 判断是否为模拟器
            if (DeviceUtils.isEmulator()) {
                return true
            }

            // 通过 JNI 本地方法检测模拟器
            if (EmulatorDetectUtil.isEmulator(contextWR.get())) {
                return true
            }
        } catch (e: Exception) {
            // 捕获异常防止崩溃，默认返回false
        }
        return false
    }

    /**
     * 是否运行在虚拟空间（多开、虚拟机、克隆环境）中
     * 通过多种手段检测，任一检测异常则返回true
     *
     * @param context Context 对象
     * @return true 表示运行在虚拟空间环境
     */
    fun isRunningInVirtualSpace(context: Context): Boolean {
        return safeCheck {
            isUidSuspicious(context)  // 检测UID是否异常（小于10000一般为系统或多开）
        } || safeCheck {
            checkHardwareInfo(context)  // 检测硬件信息异常，如MAC地址
        } || safeCheck {
            SignatureChecker.isSignatureTampered(
                context,
                SoUtil.getCDecryptedSha256(),
                SoUtil.getCDecryptedPublicKey(),
                SoUtil.getCDecryptedCN()
            )  // 检查应用签名是否被篡改
        } || safeCheck {
            checkPackageName(context)  // 反射检测包名是否被Hook或篡改
        } || safeCheck {
            readMapsByFile(context)  // 通过读取 /proc/self/maps 文件判断异常映射
        } || safeCheck {
            readMapsBySo(context)  // 通过本地so读取进程映射判断
        } || safeCheck {
            detectKnownVirtualApps(context)  // 检测已安装的已知虚拟空间类App
        } || safeCheck {
            isParentVirtualSpace()  // 检测父进程是否为虚拟空间
        } || safeCheck {
            checkSuspiciousMounts()  // 检测挂载点中是否含有虚拟环境标识
        }
    }

    /**
     * 统一安全检测包装，防止单个检测抛异常影响整体逻辑
     * @param check 检测函数
     * @return Boolean 检测结果，异常时返回false
     */
    private inline fun safeCheck(check: () -> Boolean): Boolean {
        return try {
            check()
        } catch (e: Exception) {
            Log.e("VirtualUtil", "检测方法异常: ${e.message}")
            false
        }
    }

    /**
     * 已知虚拟空间多开包名列表，可扩展
     */
    private val VIRTUAL_SPACE_PACKAGES = setOf(
        "com.cmaster.cloner",        // Cloner 多开器
        "com.lbe.parallel",          // Parallel Space
        "com.vmos",                  // VMOS 虚拟系统
        "org.app.virtual",           // Virtual App
        "de.robv.android.xposed.installer", // Xposed 安装器
        "com.excelliance.dualaid",  // 双开助手
        "com.bly.dkplat",           // 多开分身
        "com.qihoo.magic",          // 360分身大师
        "com.oem.oemclone",         // 部分国产ROM自带多开
        "com.island",               // Island 多账户
        "com.shelter"               // Shelter
    )

    /**
     * 检测父进程是否属于虚拟空间多开进程
     *
     * @return true 表示父进程属于已知虚拟空间
     */
    private fun isParentVirtualSpace(): Boolean {
        return try {
            val currentPid = Process.myPid()
            val statFile = File("/proc/$currentPid/stat")
            if (!statFile.exists()) return false

            // 读取stat文件，格式参考proc文档
            val statContent = statFile.readText().split(" ")
            if (statContent.size < 4) return false
            val ppid = statContent[3].toIntOrNull() ?: return false

            val parentCmdlineFile = File("/proc/$ppid/cmdline")
            if (!parentCmdlineFile.exists()) return false

            val parentProcessName = parentCmdlineFile.readText().trim()
            // 取包名或最后路径段
            val parentPackage = parentProcessName.substringAfterLast("/", parentProcessName)

            // 判断是否在虚拟空间已知包名列表中
            VIRTUAL_SPACE_PACKAGES.any { parentPackage == it }
        } catch (e: Exception) {
            false
        }
    }

    /**
     * 检测挂载点是否含有虚拟环境标志字符串
     * 例如包含 vmos、virtual、parallel 等关键字
     *
     * @return true 表示存在虚拟挂载点
     */
    private fun checkSuspiciousMounts(): Boolean {
        return try {
            BufferedReader(FileReader("/proc/self/cgroup")).use { br ->
                var line: String?
                while (br.readLine().also { line = it } != null) {
                    Log.e("挂载点:", line ?: "")
                    line?.let {
                        if (it.contains("vmos") || it.contains("virtual") || it.contains("parallel")) {
                            return true
                        }
                    }
                }
                false
            }
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }

    /**
     * 检测硬件信息是否异常
     * 主要检测MAC地址是否为常见的虚拟空间默认值
     *
     * @return true 表示硬件信息异常
     */
    fun checkHardwareInfo(context: Context): Boolean {
        val macAddress = getMacAddress()

        val virtualMacs = setOf(
            "00:00:00:00:00:00",
            "02:00:00:00:00:00",
            "aa:bb:cc:dd:ee:ff"
        )
        return virtualMacs.contains(macAddress.lowercase())
    }

    /**
     * 获取设备MAC地址，兼容多种环境
     *
     * 1. 遍历所有网络接口，过滤虚拟接口
     * 2. 尝试从/sys/class/net路径读取物理网卡MAC
     *
     * @return MAC地址字符串，失败返回空字符串
     */
    private fun getMacAddress(): String {
        try {
            val interfaces: Enumeration<NetworkInterface> = NetworkInterface.getNetworkInterfaces()
            while (interfaces.hasMoreElements()) {
                val ni: NetworkInterface = interfaces.nextElement()
                if (ni.isVirtual || ni.displayName.contains("virtual", ignoreCase = true)) {
                    continue
                }
                val macBytes: ByteArray? = ni.hardwareAddress
                if (macBytes != null && macBytes.isNotEmpty()) {
                    val sb = StringBuilder()
                    for (b in macBytes) {
                        sb.append(String.format("%02X:", b))
                    }
                    if (sb.isNotEmpty()) {
                        sb.deleteCharAt(sb.length - 1)
                    }
                    val mac = sb.toString()
                    if (mac.isNotBlank() && !mac.all { it == '0' || it == ':' }) {
                        return mac.lowercase()
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        try {
            val netDir = File("/sys/class/net/")
            if (netDir.exists() && netDir.isDirectory) {
                netDir.listFiles()?.forEach { nicFile ->
                    val nicName = nicFile.name
                    if (nicName.contains("docker", ignoreCase = true) ||
                        nicName.contains("vpn", ignoreCase = true) ||
                        nicName.contains("lo", ignoreCase = true)
                    ) {
                        return@forEach
                    }
                    val macFile = File(nicFile, "address")
                    if (macFile.exists() && macFile.canRead()) {
                        val mac = macFile.readText().trim().lowercase()
                        if (mac.isNotBlank() && !mac.all { it == '0' || it == ':' }) {
                            return mac
                        }
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        return ""
    }

    /**
     * 反射检测包名是否异常（是否被Hook或篡改）
     *
     * @return true 表示异常
     */
    private fun checkPackageName(context: Context): Boolean {
        try {
            val clazz = Class.forName("android.content.Context")
            val method = clazz.getMethod("getPackageName")
            val result = method.invoke(context) as String
            if (result != context.packageName) {
                return true
            }
        } catch (e: Exception) {
            // 反射异常可能是被Hook的迹象
            return true
        }
        return false
    }

    /**
     * 读取 /proc/self/maps 文件判断内存映射是否异常
     *
     * @return true 表示异常
     */
    private fun readMapsByFile(context: Context): Boolean {
        val mapsFile = File("/proc/self/maps")
        if (!mapsFile.exists() || !mapsFile.canRead()) {
            Log.e("VirtualCheck", "/proc/self/maps 不存在或不可读")
            return false
        }
        try {
            BufferedReader(FileReader(mapsFile)).use { reader ->
                var line: String?
                while (reader.readLine().also { line = it } != null) {
                    val currentLine = line!!.lowercase()
                    Log.e("VirtualCheck数据:", currentLine)
                    if (isAbnormal(currentLine, context.packageName)) {
                        return true
                    }
                }
            }
        } catch (e: IOException) {
            Log.e("VirtualCheck", "读取 /proc/self/maps 失败: ${e.message}")
            e.printStackTrace()
        }
        return false
    }

    /**
     * 通过本地so读取 /proc/self/maps 内容判断异常
     *
     * @return true 表示异常
     */
    private fun readMapsBySo(context: Context): Boolean {
        var result = false
        SoUtil.getReadProcSelfMaps()?.lines()?.forEach {
            Log.e("VBySo数据:", it)
            if (isAbnormal(it, context.packageName)) {
                result = true
                return@forEach
            }
        }
        return result
    }

    /**
     * 判断路径是否异常（检测是否有非正常的映射路径）
     *
     * @param path 进程内存映射路径字符串
     * @param packageName 应用包名
     * @return true 表示异常
     */
    private fun isAbnormal(path: String, packageName: String): Boolean {
        val tag = "/data/data/"
        if (!path.contains(packageName)) return false

        val result = path.split(packageName)
        result.forEach {
            if (it.startsWith(tag) && it.length > tag.length) {
                return true
            } else if (it.endsWith(tag)) {
                return false
            } else {
                val data = it.split(tag)
                if (data.size > 1) {
                    return true
                }
            }
        }
        return false
    }

    /**
     * 检测 App 的 UID 是否异常（正常第三方App的UID一般>=10000）
     *
     * @return true 表示异常UID（可能多开环境）
     */
    private fun isUidSuspicious(context: Context): Boolean {
        val uid = context.applicationInfo.uid
        return uid < 10000
    }

    /**
     * 通过类加载器路径检测是否包含虚拟空间关键词
     *
     * @return true 表示可疑路径
     */
    private fun isClassLoaderPathSuspicious(): Boolean {
        return try {
            val classPath = this.javaClass.classLoader?.toString()?.lowercase(Locale.getDefault()) ?: ""
            classPath.contains("virtual") || classPath.contains("xspace") ||
                    classPath.contains("dual") || classPath.contains("multi") ||
                    classPath.contains("parallel") || classPath.contains("cloner") ||
                    classPath.contains("xposed")
        } catch (e: Exception) {
            false
        }
    }

    /**
     * 检测设备中是否安装了已知虚拟空间多开App
     *
     * @return true 表示安装了已知多开App
     */
    private fun detectKnownVirtualApps(context: Context): Boolean {
        val pm = context.packageManager
        val packages = pm.getInstalledPackages(0)
        return packages.any { it.packageName in VIRTUAL_SPACE_PACKAGES }
    }

}
